import {
  type BoundingBox,
  GraphicObject,
  GraphicType,
  type Point,
  type ViewTransform,
} from '../GraphicObject';
import TransformManager from '../TransformManager';
import type { GraphicObjectData } from '../types';

export class Polygon extends GraphicObject {
  public points: Point[];
  public closed: boolean;
  static create: (
    points: Point[],
    options?: {
      transform?: any;
      style?: any;
      layerId?: string;
      closed?: boolean;
    },
  ) => Polygon;

  constructor(
    points: Point[],
    transform = GraphicObject.defaultTransform(),
    style = GraphicObject.defaultStyle(),
    layerId = 'default',
    closed = true,
  ) {
    super(GraphicType.POLYGON, transform, style, layerId);
    this.points = points.map((p) => ({ ...p })); // 深拷贝
    this.closed = closed;
  }

  draw(ctx: CanvasRenderingContext2D, viewTransform: ViewTransform): void {
    if (this.points.length < 2) return;

    ctx.save();

    // 应用视图变换
    ctx.translate(viewTransform.offset.x, viewTransform.offset.y);
    ctx.scale(viewTransform.scale, viewTransform.scale);
    ctx.rotate(viewTransform.rotation);

    // 应用对象变换
    ctx.translate(this.transform.position.x, this.transform.position.y);
    ctx.rotate(this.transform.rotation);
    ctx.scale(this.transform.scale.x, this.transform.scale.y);

    // 设置样式
    if (this.style.fillColor && this.closed) {
      ctx.fillStyle = this.style.fillColor;
    }
    if (this.style.strokeColor) {
      ctx.strokeStyle = this.style.strokeColor;
    }
    if (this.style.strokeWidth) {
      ctx.lineWidth = this.style.strokeWidth / viewTransform.scale;
    }
    if (this.style.opacity) {
      ctx.globalAlpha = this.style.opacity;
    }
    if (this.style.lineDash) {
      ctx.setLineDash(this.style.lineDash);
    }

    // 绘制多边形
    ctx.beginPath();
    ctx.moveTo(this.points[0].x, this.points[0].y);

    for (let i = 1; i < this.points.length; i++) {
      ctx.lineTo(this.points[i].x, this.points[i].y);
    }

    if (this.closed) {
      ctx.closePath();
    }

    // 填充和描边
    if (this.style.fillColor && this.closed) {
      ctx.fill();
    }
    if (this.style.strokeColor && this.style.strokeWidth) {
      ctx.stroke();
    }

    ctx.restore();
  }

  contains(point: Point): boolean {
    if (this.points.length < 3 && this.closed) return false;

    // 将世界坐标转换为对象局部坐标
    const localPoint = TransformManager.worldToLocal(point, this.transform);

    if (this.closed) {
      // 使用射线法判断点是否在多边形内
      return this.isPointInPolygon(localPoint, this.points);
    } else {
      // 对于开放多边形，检查是否靠近任何线段
      return this.isPointNearPolyline(localPoint, this.points, 5); // 5像素容差
    }
  }

  getBoundingBox(): BoundingBox {
    if (this.points.length === 0) {
      return { x: 0, y: 0, width: 0, height: 0 };
    }

    let minX = this.points[0].x;
    let maxX = this.points[0].x;
    let minY = this.points[0].y;
    let maxY = this.points[0].y;

    for (const point of this.points) {
      minX = Math.min(minX, point.x);
      maxX = Math.max(maxX, point.x);
      minY = Math.min(minY, point.y);
      maxY = Math.max(maxY, point.y);
    }

    return {
      x: this.transform.position.x + minX,
      y: this.transform.position.y + minY,
      width: (maxX - minX) * this.transform.scale.x,
      height: (maxY - minY) * this.transform.scale.y,
    };
  }

  /**
   * 序列化对象数据
   */
  serialize(): GraphicObjectData {
    return {
      id: this.id,
      type: this.type,
      transform: { ...this.transform },
      style: { ...this.style },
      layerId: this.layerId,
      points: this.points.map((p) => ({ ...p })),
      closed: this.closed,
    };
  }

  // 射线法判断点是否在多边形内
  private isPointInPolygon(point: Point, polygon: Point[]): boolean {
    let inside = false;
    for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
      const xi = polygon[i].x,
        yi = polygon[i].y;
      const xj = polygon[j].x,
        yj = polygon[j].y;

      const intersect =
        yi > point.y !== yj > point.y &&
        point.x < ((xj - xi) * (point.y - yi)) / (yj - yi) + xi;

      if (intersect) inside = !inside;
    }
    return inside;
  }

  // 判断点是否靠近折线
  private isPointNearPolyline(
    point: Point,
    polyline: Point[],
    tolerance: number,
  ): boolean {
    for (let i = 0; i < polyline.length - 1; i++) {
      const p1 = polyline[i];
      const p2 = polyline[i + 1];

      if (this.distanceToSegment(point, p1, p2) <= tolerance) {
        return true;
      }
    }
    return false;
  }

  // 计算点到线段的距离
  private distanceToSegment(point: Point, p1: Point, p2: Point): number {
    const A = point.x - p1.x;
    const B = point.y - p1.y;
    const C = p2.x - p1.x;
    const D = p2.y - p1.y;

    const dot = A * C + B * D;
    const lenSq = C * C + D * D;
    let param = -1;

    if (lenSq !== 0) param = dot / lenSq;

    let xx: number, yy: number;

    if (param < 0) {
      xx = p1.x;
      yy = p1.y;
    } else if (param > 1) {
      xx = p2.x;
      yy = p2.y;
    } else {
      xx = p1.x + param * C;
      yy = p1.y + param * D;
    }

    const dx = point.x - xx;
    const dy = point.y - yy;
    return Math.sqrt(dx * dx + dy * dy);
  }
}

// 静态创建方法
Polygon.create = (
  points: Point[],
  options?: {
    transform?: any;
    style?: any;
    layerId?: string;
    closed?: boolean;
  },
) => {
  return new Polygon(
    points,
    options?.transform,
    options?.style,
    options?.layerId,
    options?.closed,
  );
};
